﻿using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;

namespace Creventive.Wpf.ShellFactory.Markup
{
    /// <summary>
    ///   Helper class for textbox extensions
    /// </summary>
    public static class TextBoxService
    {
        #region Static Fields/Constants

        /// <summary>
        /// Backing field for the TreatEnterAsTab dependency property
        /// </summary>
        public static readonly DependencyProperty TreatEnterAsTabProperty = DependencyProperty.RegisterAttached("TreatEnterAsTab", typeof(bool), typeof(TextBoxService), new UIPropertyMetadata(false, (s, e) => NotifyTreatEnterAsTabChanged((FrameworkElement)s, (bool)e.OldValue, (bool)e.NewValue)));

        /// <summary>
        /// Backing field for the AutoSelectOnFocus dependency property
        /// </summary>
        public static readonly DependencyProperty AutoSelectOnFocusProperty = DependencyProperty.RegisterAttached("AutoSelectOnFocus", typeof(bool), typeof(TextBoxService), new UIPropertyMetadata(false, (s, e) => NotifyAutoSelectOnFocusChanged((FrameworkElement)s, (bool)e.OldValue, (bool)e.NewValue)));

        #endregion

        /// <summary>
        ///   Gets a value determining if all content should be selected whenever the component gets focused.
        /// </summary>
        /// <param name = "obj">The obj.</param>
        /// <returns></returns>
        public static bool GetAutoSelectOnFocus(DependencyObject obj)
        {
            return (bool)obj.GetValue(AutoSelectOnFocusProperty);
        }

        /// <summary>
        ///   Sets a value determining if all content should be selected whenever the component gets focused.
        /// </summary>
        /// <param name = "obj">The obj.</param>
        /// <param name = "value">if set to <c>true</c>, all content is selecteed whenever the component gets focused.</param>
        public static void SetAutoSelectOnFocus(DependencyObject obj, bool value)
        {
            obj.SetValue(AutoSelectOnFocusProperty, value);
        }

        // Using a DependencyProperty as the backing store for AutoSelectOnFocus.  This enables animation, styling, binding, etc...

        private static void NotifyAutoSelectOnFocusChanged(FrameworkElement element, bool oldValue, bool newValue)
        {
            if (oldValue == newValue)
                return;

            if (newValue)
                InstallFocusSelector(element);
            else
                UninstallFocusSelector(element);
        }

        private static void UninstallFocusSelector(FrameworkElement element)
        {
            if ((element is TextBoxBase) || (element is PasswordBox))
                element.GotFocus -= FocusSelector_GotFocus;
        }

        private static void InstallFocusSelector(FrameworkElement element)
        {
            if ((element is TextBoxBase) || (element is PasswordBox))
                element.GotFocus += FocusSelector_GotFocus;
        }

        private static void FocusSelector_GotFocus(object sender, RoutedEventArgs e)
        {
            var element = e.OriginalSource as FrameworkElement;
            if (element is TextBoxBase)
                ((TextBoxBase)element).SelectAll();
            else if (element is PasswordBox)
                ((PasswordBox)element).SelectAll();
        }

        /// <summary>
        ///   Gets a value determining whether the Enter key should be treated as the Tab key.
        /// </summary>
        /// <param name = "obj">The obj.</param>
        /// <returns><c>true</c>if the Enter key will be treated the same way as the Tab key.</returns>
        public static bool GetTreatEnterAsTab(FrameworkElement obj)
        {
            return (bool)obj.GetValue(TreatEnterAsTabProperty);
        }

        /// <summary>
        ///   Sets a value determining whether the Enter key should be treated as the Tab key.
        /// </summary>
        /// <param name = "obj">The obj.</param>
        /// <param name = "value">if set to <c>true</c>, the Enter key will be treated the same way as the Tab key.</param>
        public static void SetTreatEnterAsTab(FrameworkElement obj, bool value)
        {
            obj.SetValue(TreatEnterAsTabProperty, value);
        }

        private static void NotifyTreatEnterAsTabChanged(FrameworkElement element, bool oldValue, bool newValue)
        {
            if (oldValue == newValue)
                return;

            if (newValue)
                InstallEnterTranslator(element);
            else
                UninstallEnterTranslator(element);
        }

        private static void UninstallEnterTranslator(FrameworkElement element)
        {
            element.PreviewKeyDown -= InstallEnterTranslator_PreviewKeyDown;
        }

        private static void InstallEnterTranslator(FrameworkElement element)
        {
            element.PreviewKeyDown += InstallEnterTranslator_PreviewKeyDown;
        }

        private static void InstallEnterTranslator_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            var element = e.OriginalSource as FrameworkElement;

            if (e.Key == Key.Enter)
            {
                element.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
                e.Handled = true;
            }
        }
    }
}